package com.sway.loadmanager
{
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.utils.Dictionary;
	public class LoadManager
	{
		private static var _instance:LoadManager;
		private static var _allowInstantiation:Boolean = false; 
		
		private var _max_connections:int = 5;
		
		private var _free_connections_pool:Array;
		private var _busy_connections_pool:Array;
		
		private var _loaded_dict:Dictionary;
		
		private var _queue_loading:Dictionary;
		private var _names:Object;
		private var _loaders:Dictionary;
		
		
		public static function getInstance():LoadManager{
			if (!_instance){
				_allowInstantiation = true;
				_instance = new LoadManager();
				_allowInstantiation = false;
			}
			return _instance;
		}
		
		public function LoadManager()
		{
			if (!_allowInstantiation){
				throw new Error("It is Singletone. Use getInstance() instead.");
			}
			_loaded_dict = new Dictionary();
			_queue_loading = new Dictionary();
			_names = new Object();
			_loaders = new Dictionary();
			_free_connections_pool = new Array();
			_busy_connections_pool = new Array();
		}
		
		
		// load resource from url and assign name if exists to it
		public function loadResource(p_url:String,p_name:String=null):Spite{
			var res:Sprite = new Sprite();
			// if resource allready loaded
			if (_loaded_dict[p_url]!=null){	
				res.addChild(new (_loaded_dict[p_url] as Class)());
				return res;
			}
			// if resource in loading queue, add it to queue
			if (_queue_loading[p_url]!=null){
				var queue:Object = _queue_loading[p_url];
				queue.list.push(res);
				return res;
			}
			
			// creating new loader
			var loader:Loader = getLoaderFromPool();
			
			// add loader to queue
			var q:Object = new Object();
			q.list = new Array();
			q.list.push(res);
			q.loader = loader;
			q.name = p_name;
			
			// link name and loader with url
			_names[p_name] =p_url; 
			_loaders[loader]=p_url;
			
			_queue_loading[p_url] = q;
			q.load(new URLRequest(p_url));
			busyLoader(loader);
			return ret;			
		}
		
		private function onSecurityError(error:SecurityErrorEvent):void{
			trace("There is Security error at: ",_loaders[error.currentTarget.loader]);
			freeLoader(error.currentTarget.loader);
		}
		
		private function onIOError(error:IOErrorEvent):void{
			trace("There is IO error at: ",_loaders[error.currentTarget.loader]);
			freeLoader(error.currentTarget.loader);
		}
		
		// todo: finish this function of cleaning loader from events
		private function cleanFromListeners(obj:Loader):void{
			if (obj.hasEventListener(SecurityErrorEvent)){
				
			}
		}
		
		// this function handle COMPLETE event of each loader
		private function onLoaderComplete(event:Event):void{
			var contentLoader:Loader = event.currentTarget.loader as Loader;
			var url:String = _loaders[contentLoader];
			delete _loaders[contentLoader];
			
			var q:Object = _queue_loading[url];
			delete _queue_loading[url];
			_loaded_dict[url]=contentLoader.content;
			for each(var sp:Sprite in q.list){
				// todo: add not content but clone of it
				sp.addChild(contentLoader.content);
			}
			
			freeLoader(contentLoader);			
		}
		
		// this function return loader if it has or null if loaders count more than limit
		private function getLoaderFromPool():Loader{
			if (_busy_connections_pool.length >0) return _busy_connections_pool[0];
			if (_free_connections_pool.length< _max_connections){
				var loader:Loader = new Loader();
				_busy_connections_pool.push(loader);
				
				loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
				loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
				
				return loader;
			}
			return null;
		}
		
		// move loader from free status to busy status in pool
		private function busyLoader(p_loader:Loader):void{
			var pos:int = -1;
			for (var i:int=0; i<_free_connections_pool.length; i++){
				if (_free_connections_pool[i]==p_loader){
					pos = i;
					break;
				}
			}
			if (pos==-1){
				throw new Error("Loader`s pool has not such loader");
			}
			_busy_connections_pool.push(_free_connections_pool[pos]);
			_free_connections_pool.pop(_free_connections_pool[pos]);
		}
		
		// move Loader from busy status to free status in pool
		private function freeLoader(p_loader:Loader):void{
			var pos:int = -1;
			for (var i:int=0; i<_busy_connections_pool.length; i++){
				if (_busy_connections_pool[i]==p_loader){
					pos = i;
					break;
				}
				if (pos==-1){
					throw new Error("There is no busy loader for free");
				}
				_free_connections_pool.push(_busy_connections_pool[pos]);
				_busy_connections_pool.pop(_busy_connections_pool[pos]);
			}
		}
		
	}
}
